#pragma once

#include "sfc/alloc.h"

namespace sfc::collections {

template <class T>
class [[nodiscard]] VecSet {
  Vec<T> _inn = {};

 public:
  VecSet() noexcept = default;

  ~VecSet() = default;

  VecSet(VecSet&&) noexcept = default;

  VecSet& operator=(VecSet&&) noexcept = default;

  auto len() const -> usize {
    return _inn.len();
  }

  auto capacity() const -> usize {
    return _inn.capacity();
  }

  auto is_empty() const -> bool {
    return _inn.is_empty();
  }

  auto items() const -> Slice<const T> {
    return _inn.as_slice();
  }

  auto items_mut() -> Slice<T> {
    return _inn.as_mut_slice();
  }

  void reserve(usize additional) {
    _inn.reserve(additional);
  }

  auto get(const auto& key) const -> Option<const T&> {
    return _inn.iter().find([&](const T& t) { return key == t; });
  }

  auto get_mut(const auto& key) -> Option<T&> {
    return _inn.iter_mut().find([&](const T& t) { return key == t; });
  }

  auto operator[](const auto& key) const -> const T& {
    auto opt = this->get(key);
    assert_fmt(opt, "VecSet::[]: key(=`{}`) not exists", key);
    return *opt;
  }

  auto operator[](const auto& key) -> T& {
    auto opt = this->get_mut(key);
    assert_fmt(opt, "VecSet::[]: key(=`{}`) not exists", key);
    return *opt;
  }

  void insert(T val) {
    if (auto pos = this->get_mut(val)) {
      *pos = static_cast<T&&>(val);
    } else {
      _inn.push(static_cast<T&&>(val));
    }
  }

  auto remove(const auto& key) -> bool {
    const auto ret = _inn.find_if([&](auto&& val) { return key == val; })  //
                         .map([&](auto idx) { _inn.remove(*idx); });
    return bool(ret);
  }

  void clear() {
    _inn.clear();
  }

  auto contains(const auto& key) const -> bool {
    return bool(this->get(key));
  }

  void fmt(auto& f) const {
    _inn.fmt(f);
  }
};

}  // namespace sfc::collections
